﻿#include "metatype.h"

#include "variant.h"

Variant::Variant(const void *data, MetaTypeId type):Variant()
{
    *this = FromValue(data,type);
}

Variant::Variant()
{
    type_id = 0;
}

Variant::Variant(const Variant &other):Variant()
{
    m_data = other.m_data;
    type_id = other.type_id;
}

Variant::Variant(const int &v):Variant()
{
    *this = FromValue(v);
}

Variant::Variant(const double &v):Variant()
{
    *this = FromValue(v);
}

Variant::Variant(const float &v):Variant()
{
    *this = FromValue(v);
}

Variant::Variant(const std::string &v):Variant()
{
    *this = FromValue(v);
}

Variant::Variant(const std::wstring &v):Variant()
{
    *this = FromValue(v);
}

Variant::Variant(const VariantList &list)
{
    *this = FromValue(list);
}

Variant::Variant(const VariantWMap &map)
{
    *this = FromValue(map);
}

Variant &Variant::operator =(const Variant &other)
{
    m_data = other.m_data;
    type_id = other.type_id;
    return *this;
}

bool Variant::operator ==(const Variant &other) const
{
    if(type_id != other.type_id){
        return false;
    }

    if(type_id == 0){
        return true;
    }

    MetaType type(type_id);

    if(type.isValid()){
        return type.Equals(m_data.get(),other.m_data.get());
    }

    return false;
}

bool Variant::isValid() const
{
    return type_id != 0;
}

MetaTypeId Variant::TypeId() const
{
    return type_id;
}

std::string Variant::TypeName() const
{
    if(type_id == 0){
        return "inValid";
    }

    MetaType meta(type_id);
    if(!meta.isValid()){
        return "";
    }

    return meta.Name();
}

bool Variant::Convertable(MetaTypeId targetId)
{
    if(type_id == 0){
        return false;
    }

    MetaType meta(type_id);
    return meta.IsConvertable(targetId);
}

bool Variant::Convert(MetaTypeId targetId)
{
	if (targetId == this->type_id) {
		return true;
	}

	if (!Convertable(targetId)) {
		return false;
	}

	Variant v = Variant::FromType(targetId);

	bool ok;
	Value(v.data(), targetId, ok);
	if (ok) {
		*this = v;
	}

	return ok;
}

void * Variant::BeReadyForReturn(Variant & var, MetaTypeId targetid)
{
	if (targetid == MetaType::IdFromType<Variant>())
	{
		return &var;
	}
	else if (targetid == MetaType::BT_None) {
		return NULL;
	}
	else if (targetid != var.type_id) {
		var = Variant::FromType(targetid);
		return var.data();
	}
}

const void* Variant::BeReadyForParam(Variant & var,MetaTypeId targetid) {

	if (targetid == MetaType::BT_Variant)
	{
		return &var;
	}
	else if (targetid == MetaType::BT_None) {
		return NULL;
	}
	else if (targetid != var.type_id) {
		if (var.Convertable(targetid)) {
			var.Convert(targetid);
			return var.data();
		}
		else {
			return NULL;
		}
		
	}
	else {
		return var.data();
	}
}

void *Variant::data()
{
    return m_data.get();
}

const void *Variant::data() const
{
    return m_data.get();
}

void Variant::AsignTo(void *data)
{
    if(type_id == 0){
        return;
    }

    MetaType meta(type_id);

    meta.Assign(data,this->data());
}

Variant Variant::FromType(MetaTypeId typeId)
{
    Variant result;

	if (typeId == MetaType::IdFromType<Variant>()) {
		return Variant();
	}

    MetaType meta(typeId);

    if(!meta.isValid()){
        return Variant();
    }

    void * data = meta.Create(NULL,NULL);

    result.m_data.reset(data,[=](void* data){
        MetaType meta(typeId);
        meta.Delete(data);
    });

    result.type_id = typeId;

    return result;
}

Variant Variant::FromValue(const void *value, int typeId)
{
    Variant result;

	if (typeId == MetaType::IdFromType<Variant>())
	{
		Variant* var = (Variant*)value;
		return *var;
	}

    MetaType meta(typeId);

    if(!meta.isValid()){
        return Variant();
    }

	

    void * data = meta.Create(NULL,value);

    result.m_data.reset(data,[=](void* data){
        MetaType meta(typeId);
        meta.Delete(data);
    });

    result.type_id = typeId;

    return result;
}

void Variant::Value(void * output, int typeId, bool & ok) const
{
	ok = false;

	if (type_id == MetaType::BT_None) {
		return;
	}

	MetaType meta(type_id);
	if (!meta.isValid()) {
		return;
	}

	if (!meta.IsConvertable(typeId)) {
		return;
	}

	meta.Convert(m_data.get(), typeId, output);
}

DataOutputStream &operator <<(DataOutputStream &s, const Variant &t)
{
    MetaType meta(t.TypeId());

    if(meta.isValid() && !meta.hasStreamOperator()){
        std::cerr << "Variant:type has not registered stream operator <<" << std::endl;
        return s;
    }

    if(meta.isNativeType()){
        char n = 'N';
        s << n;
        s << t.TypeId();
    }
    else{
        char n = 'C';
        s << n;
        s << t.TypeName();
    }

    if(meta.isValid()){
        meta.Output(s,t.data());
    }

    return s;
}

DataInputStream &operator >>(DataInputStream &s, Variant &t) throw (StreamException)
{
    char n;
    s >> n;

    MetaType meta;
    if(n == 'C')
    {
        std::string name;
        s >> name;
        meta = MetaType(name);
    }
    else if(n == 'N'){
        int id;
        s >> id;
        meta = MetaType(id);
    }
    else{
        throw StreamException("Invalid Content");
    }

    if(meta.isValid() && !meta.hasStreamOperator()){
        std::cerr << "Variant:type has not registered stream operator >>" << std::endl;
        return s;
    }

    if(meta.isValid()){
        t = Variant::FromType(meta.Id());
        meta.Input(s,t.data());
    }
    else{
        t = Variant();
    }

    return s;
}
